package com.floretexaminaction.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.floretexaminaction.dao.ExaminationRecordDao;
import com.floretexaminaction.model.ExaminationRecordEntity;
import com.floretexaminaction.model.StudentAnswerBankEntity;
import com.floretexaminaction.model.StudentTestAnswersEntity;
import com.floretexaminaction.redis.Redis;
import com.floretexaminaction.service.UserService;
import com.floretexaminaction.timer.ThreadTimer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.annotations.LazyToOneOption;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.Jedis;

import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.lang3.StringUtils;

@RestController
@Slf4j
public class StudentController {

    Log log = LogFactory.getLog("三岁小仙仙");

    Jedis redis = Redis.getRedis();

    @Autowired
    ExaminationRecordDao examinationRecordDao;

    @Autowired
    UserService userService;

    /**
     * 根据班级号，获得学生的考试信息
     * @param classNumber 班级号
     * @return 数组
     * @throws ParseException 日期时间转换类异常
     */
    @PostMapping("/autoGetExaminationInfo")
    public synchronized String autoGetExaminationInfo(String classNumber, String studentNumber) throws ParseException {
//        List<String> list = examinationRecordDao.findStudentExamination(className);
        List list = new ArrayList();
        if (redis.exists(classNumber)){
            String jsonString = redis.get(classNumber);
            JSONArray jsonArray = JSONArray.parseArray(jsonString);
            for (int i = 0 ; i < jsonArray.size() ; i++){
                JSONObject jsonObject = JSONObject.parseObject(jsonArray.get(i).toString());
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
                Date endTime = sdf.parse(jsonObject.get("endTime").toString());
                // 判断该用户所在班级 是否 有考试科目 处于 待开考状态 并返回
                // 结束时间 大于 当前时间 表示 有待开考科目
                // 反之 于小 则表示 已经过了该科目的考试时间
                if(endTime.getTime() > new Date().getTime()){
                    StringBuffer sb = new StringBuffer();
                    sb.append(jsonObject.get("coursesName")).append(",");
                    sb.append(jsonObject.get("startTime")).append(",");
                    sb.append(jsonObject.get("endTime"));
                    Date startTime = sdf.parse(jsonObject.get("startTime").toString());
                    // 判断 该科目的开始时间 是否 小于等于 当前时间
                    // 若开始时间 小于等于 当前时间 表示该科目已经开考 返回试题库号
                    if(startTime.getTime() <= new Date().getTime()){
                        // 在为用户返回试题号前，判断一下用户是否已经完成答题了，这个情况，开发测试中
                        // 发现用户提交后，返回主页面，用户在考试结束前 依然可以拿到题，我们需要在这一步判断用户
                        // 是否已经作答过，进行进一个处理 根据班级号和试题库号 查询该答案记录表中是否有该记录作答记录
                        StringBuffer key = new StringBuffer();
                        key.append(classNumber);
                        key.append("-");
                        key.append(jsonObject.get("questionBank"));
                        // 判断本班学生作答记录表是否存在
                        if (redis.exists(key.toString())){
                            // 若存在 判断该用户是否已经作答
                            String result = redis.get(key.toString());
                            JSONArray jsonArray1 = JSONArray.parseArray(result);
                            boolean flag = true;
                            for (int s = 0 ; s < jsonArray1.size() ; s++){
                                JSONObject jsonObject1 = JSONObject.parseObject(jsonArray1.get(s).toString());
                                if(studentNumber.equals(jsonObject1.get("studentNumber"))){
                                    sb.append(",").append("isAnswer");
                                    flag = false;
                                    break;
                                }
                            }
                            if(flag){sb.append(",").append(jsonObject.get("questionBank"));};
                        }else{
                            sb.append(",");
                            sb.append(jsonObject.get("questionBank"));
                        }
                    }else{
                        sb.append(",").append("noExamination");
                    }
                    list.add(sb.toString());
                }
            }
        }
        return JSON.toJSONString(list);
    }

    /**
     * 根据班级号、试题库号（具有唯一性） 获取对应的考试试题信息
     * @param classNumber 班级号
     * @param questionBank 试题库号
     * @return 数组对象
     *
     * 进入该方法，表明已经有课程考试已经开考，用户拿着自己的班级号和试题库号，请求获取试题信息
     */
    @PostMapping("/autoGetExaminationQuestions")
    public String autoGetExaminationQuestions(String classNumber ,String questionBank){
        synchronized(this){
            if (redis.exists(classNumber)){
                // 获取字符串类型的json数组对象
                // 转换为json数组
                JSONArray jsonArray = JSONArray.parseArray(redis.get(classNumber));
                for (int i = 0 ; i < jsonArray.size() ; i++){
                    // 将json数组内的对象转换为java对象
                    JSONObject jsonObject = JSONObject.parseObject(jsonArray.get(i).toString());
                    // 循环缓存中记录的试题库号 是否 与用户上传的一样
                    //if(jsonObject.get("questionBank").toString().equals(questionBank)){}
                    // 判断该试题库还是否存在，因为试题库有过期时间，若有用户在考试结束后，刷新页面
                    // 前端将会继续拿着试题库号来获取试题，但，当下已经过了考试时间，试题库已经不存在了
                    // 所示是获取不了试题库的，需要给用户返回一个空数组，前端自动处理
                    // 若不加以判断处理，则服务端会出错报500
                    if(!redis.exists(questionBank)){
                        return "[]";
                    };
                    // 若存在，根据试题库号获取对应的所有试题信息
                    // 转换为json数组对象
                    JSONArray parseArray = JSONArray.parseArray(redis.get(questionBank));
                    for (int j = 0 ; j < parseArray.size() ; j++){
                        JSONObject object = JSONObject.parseObject(parseArray.get(j).toString());
                        // 将正确答案置空，并返回
                        object.put("rightOption","");
                        parseArray.remove(j);
                        parseArray.add(j, object);
                    }
                    // 返回所有试题信息
                    return JSON.toJSONString(parseArray);
                }
            }
        }
        return "[]";
    }

    //public static void main(String[] args) {
    //    Lock lock = new ReentrantLock();
    //    Jedis redis = new Jedis("localhost");
    //    redis.set("number", "1000");
    //    for (int i = 0 ; i < 1000 ; i++){
    //        Thread thread = new Thread(new Runnable() {
    //            @Override
    //            public void run() {
    //                lock.lock();
    //                int number = Integer.valueOf(redis.get("number"));
    //                number--;
    //                redis.set("number", String.valueOf(number));
    //                System.out.println(redis.get("number"));
    //                lock.unlock();
    //            }
    //        });
    //        thread.start();
    //    }
    //}

    /**
     * 保存学生提交的作答信息
     * @param data 作答数据
     * @return
     * @throws ParseException
     */
    @PostMapping("/submitExaminationInfo")
    public synchronized boolean submitExaminationInfo(String data) throws ParseException {
        // 转换字符类型的json对象
        JSONObject jsonObject = JSONObject.parseObject(data);
        // 提取用户提交数据中的试题库号，判断该试题库是否存在
        // 若不存在，表示已经过了考试最后时间
        // 若存在，表示当前考试未结束
        if (!redis.exists(jsonObject.get("questionBank").toString())){
            // 判断不存在，直接驳回用户提交作答信息，本科目做0分处理
            // 后面的代码段不再执行
            return false;
        }
        // 1.创建学生答案实体类
        StudentTestAnswersEntity sta = new StudentTestAnswersEntity();
        // 2.设置班级号
        sta.setClassNumber(jsonObject.get("classNumber").toString());
        // 3.设置学号
        sta.setStudentNumber(jsonObject.get("studentNumber").toString());
        // 4.设置课程名
        sta.setQuestionBank(jsonObject.get("questionBank").toString());
        // 5.设置正确答案
        sta.setRightOption(jsonObject.get("rightOption").toString());
        // 6.将每位用户提交的答案进行缓存 创建一个Key 名为班级号-试题库
        StringBuffer key = new StringBuffer();
        key.append(sta.getClassNumber());
        key.append("-");
        key.append(sta.getQuestionBank());
        // 7.判断是否存在该答案存储库 - 其是否创建取决于 前端提交作答的首位用户
        // 将实体类 转换 java对象
        JSONObject object = JSONObject.parseObject(JSONObject.toJSONString(sta, SerializerFeature.WriteMapNullValue));
        object.remove("questionBank");
        object.remove("classNumber");
        if(redis.exists(key.toString())){
            // 缓存中存在该作答记录表
            String jsonString = redis.get(key.toString());
            JSONArray jsonArray = JSONArray.parseArray(jsonString);
            jsonArray.add(object);
            redis.set(key.toString(), JSON.toJSONString(jsonArray));
        }else{

            /**
             * 在这里开启一个线程，具体原因如下：
             * 当一们考试开启时，用户会拿自己的到班级号和试题库号来获取试题
             * 当有一个用户首先完成作答时，用户提交的作答信息，将会到该方法submitExaminationInfo
             * 该方法会将用户的作答信息缓存到redis中，这里需要在缓存中建一个表，用来存储该班级的学生作答
             * 这时，会判断缓存中是否有该班级的作答信息表，若 不存在
             * 就会在该else段内执行 创建一个作答表，若下次本班学生提交，再次判断
             * 已经存在该班的作答表，不会再创建，这时，这个else，里的代码在一个班级内一门考试当且仅当执行一次
             * 重点来了
             * 一个班级内一门考试在第一次也是最后一次 执行该else代码段时，开启一个处理线程
             * 在这里获取该门考试的结束时间，在线程里设置一个定时器，定时器结束时间为考试结束时间，同时线程结束
             * 在定时器结束前。
             * 将本次本次考试科目的作答记录持久化--存储数据库
             * 并计算出每一个学生成绩，将成绩存入班级学生表中的对应考试科目字段中
             * 程序结束
             */
            String classExaminationRecord = redis.get(sta.getClassNumber());
            JSONArray jsonArray = JSONArray.parseArray(classExaminationRecord);
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
            Date endTime = new Date();
            for (int i = 0 ; i < jsonArray.size() ; i++){
                JSONObject jsonObject1 = JSONObject.parseObject(jsonArray.get(i).toString());
                if(sta.getQuestionBank().toString().equals(jsonObject1.get("questionBank").toString())){
                    endTime = sdf.parse(jsonObject1.get("endTime").toString());
                }
            }

            // 进入该else 表明 缓存中不存在该作答记录表，将用户第一个用户的作答记录保存，并获取到该考试的科目的
            // 结束时间，将这个与当前时间计算出相差的秒数，作为缓存库的过期时间
            StringBuffer sb = new StringBuffer("[").append(JSON.toJSONString(object)).append("]");
            redis.set(key.toString(), sb.toString());
            // 结束时间 - 当前时间 除 1000 得出秒数
            // 60*10 表示 让该缓存多保存10分钟
            long l = Math.round((endTime.getTime() - new Date().getTime()) / 1000) + 60 * 10;
            redis.expire(key.toString(), (int)l);
            // 开启定时器，监听作答库过期时间
            ThreadTimer threadTimer = new ThreadTimer(sta, userService, endTime);
        }
        return true;
    }

    /**
     * 获取考试成绩
     * @param classNumber 班级号
     * @param studentNumber 学号
     * @return
     */
    @PostMapping("/autoGetStudentExaminationRecord")
    public synchronized String autoGetStudentExaminationRecord(String classNumber, String studentNumber){
        String jsonString = userService.queryClassStudent(classNumber, studentNumber);
        JSONArray jsonArray = JSONArray.parseArray(jsonString);
        List retList = new ArrayList();
        for (int i = 0 ; i < jsonArray.size() ; i++){
            JSONObject jsonObject = JSONObject.parseObject(jsonArray.get(i).toString());
            // 移除学生基本信息
            jsonObject.remove("studentName");
            jsonObject.remove("studentNumber");
            jsonObject.remove("studentPass");
            jsonObject.remove("studentEmail");

            // 移除学生成绩为空的项 为空表示未参试-或考试未开始
            Set<String> strings = jsonObject.keySet();
            List list = new ArrayList();
            for (String string : strings) {
                if (jsonObject.get(string).toString().equals("null")){
                    list.add(string);
                }
            }
            list.forEach(item->{
                jsonObject.remove(item);
            });

            // 经过以上去除，以下获取的就是对应的科目和成绩了
            Set<String> strings1 = jsonObject.keySet();
            for (String string : strings1){
                List list1 = new ArrayList();
                list1.add(string);
                list1.add(jsonObject.get(string));
                retList.add(list1);
            }
        }
        System.out.println(retList);
        return JSON.toJSONString(retList);
    }

}






